<?php
	/**
	 * <version greek="beta" major="1" minor="0">Cochin Splash Hen</version>
	 * <author email="njones@art-k-tec.com">Nika Jones</author>
	 * <copyright start="2007">Art-k-tec Systems, LLC</copyright>
	 * <license type="Apache2.0" href="http://www.opensource.org/licenses/apache2.0.php">Apache License, Version 2.0</license>
	 */
	class bQueueException {
		const INVALID_PRIORITY =0;
		const INVALID_QUEUE_TYPE =0;
	}
	
	/// <summary>
	///  A class that allows for quing of data
	/// </summary>
	class bQueue implements IteratorAggregate, Countable {
		private static $nMajor =1;
		private static $nMinor =0;
		private static $sRevision ="0000";
		
		const FIFO 			=0;
		const FILO			=1;
		const FIFO_PRIORITY	=2;
		const FILO_PRIORITY	=4;
		
		const PRIORITY_LOW =0.0;
		const PRIORITY_HIGH =1.0;
		
		private $sType;
		private $aQueue;
		
		/// <summary>
		///  constructor
		/// </summary>
		public function __construct($cType =self::FIFO) {
			
			if(!is_numeric($cType) || ($cType > 4 && $cType > 0)) {
				
				throw new bQueueException(
					'You must pass a valid queue type.',
					0);
			}
			$this->aQueue =array();
			$this->sType =$cType;
		}
		
		/// <summary>
		///  Return the version of this object, mainly for package management
		/// </summary>
		public static function version() { return self::$nMajor.'.'.self::$nMinor.'.'.self::$sRevision; }
		
		/// <summary>
		///  Sets the array that will be iterated over
		/// </summary>
		public function getIterator() {

			switch($this->sType) {
				
				case self::FIFO:
					$oIterator =new ArrayIterator($this->aQueue);
				break;
				
				case self::FILO:
					
					$aQueueReverse =$this->aQueue;
					krsort($aQueueReverse);
					$oIterator =new ArrayIterator($aQueueReverse);
				break;

				case self::FIFO_PRIORITY:
					
					$aQueue =array();
					$aQueuePriority =$this->aQueue;
					krsort($aQueuePriority);
					foreach($aQueuePriority as $a) { $aQueue =array_merge($aQueue, $a); }
					
					$oIterator =new ArrayIterator($aQueue);
				break;
				
				case self::FILO_PRIORITY:
						
					$aQueue =array();
					$aQueuePriority =$this->aQueue;
					ksort($aQueuePriority);
					foreach($aQueuePriority as $a) { $aQueue =array_merge($aQueue, $a); }

					$oIterator =new ArrayIterator($aQueueReverse);
				break;
			}

			$this->aQueue =array(); // clears the queue --make sure this is the correct behaviour.
			return $oIterator;
		}
		
		/// <summary>
		///  The interface method for counting with Countable of the SPL
		/// </summary>
		public function count() {
			
			return count($this->aQueue);
		}
		
		/// <summary>
		///  Add an item to the queue
		/// </summary>
		/// <param type="mixed" name="mData">
		///  The item that is to be placed in the queue
		/// </param>
		/// <param type="int" name="nPriority" optional="true">
		///  The numeric priority of the queued item
		/// </param>
		public function EnQueue($mData, $nPriority =null) {
			
			switch($this->sType) {
				case self::FIFO:
				case self::FILO: // drop
					$this->aQueue[] =$mData;
				break;
				
				case self::FIFO_PRIORITY:
				case self::FILO_PRIORITY: // drop
					if(!is_numeric($nPriority)) {
						
						throw new bQueueException(
							'The priority must be a number between '.self::PRIORITY_LOW.' and '.self::PRIORITY_HIGH
						, 0);
					}
					
					$sPriority =sprintf("%1.1f", $nPriority);
					$this->aQueue[$sPriority][] =$mData;
				break;
			}
			
			return true;
		}
		
		/// <summary>
		///  Remove the item from the queue
		/// </summary>
		public function DeQueue() {
			$mReturn =false;
			
			switch($this->sType) {
				
				case self::FIFO:
					$mReturn =array_shift($this->aQueue);
				break;
				
				case self::FILO:
					$mReturn =array_pop($this->aQueue);
				break;
				
				case self::FIFO_PRIORITY:
					
					$sPriority =$this->PrioritySeek();
					
					if($sPriority !='') {
						
						$mReturn =array_shift($this->aQueue[$sPriority]);
						if(count($this->aQueue[$sPriority]) ==0) { unset($this->aQueue[$sPriority]); }
					}
					
				break;
				
				case self::FILO_PRIORITY:
				
					$nPriority =$this->PrioritySeek();
					$mReturn =array_pop($this->aQueue[$nPriority]);
					
					if(count($this->aQueue[$nPriority]) ==0) { unset($this->aQueue[$nPriority]); }
				break;
			}
			
			return $mReturn;
		}
		
		/// <summary>
		///  Look at the next item in the queue without taking it out
		///  of the queue
		/// </summary>
		public function Peek() {
			
			switch($this->sType) {
				
				case self::FIFO:
					$mReturn =$this->aQueue[0];
				break;
				
				case self::FILO:
					$mReturn =$this->aQueue[count($this->aQueue) -1];
				break;
				
				case self::FIFO_PRIORITY:
					
					$nPriority =$this->PrioritySeek();
					$mReturn =$this->aQueue[$nPriority][0];
				break;
				
				case self::FILO_PRIORITY:
					
					$nPriority =$this->PrioritySeek();
					$mReturn =$this->aQueue[$nPriority][count($this->aQueue[$nPriority]) -1];
				break;
			}

			return $mReturn;
		}
		
		/// <summary>
		///  Removes all of the items from the queue and starts fresh
		/// <summary>
		public function Clear() {
			
			$this->aQueue =array();
		}
		
		/// <summary>
		///  Returns the next higest priority item in the queue
		/// </summary>
		private function PrioritySeek() {
			
			$aPriority =array_keys($this->aQueue);
			rsort($aPriority);
			$nPriority =array_shift($aPriority);
			
			return $nPriority;
		}
	}
?>